/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Mouse.h"
#include <sstream>

namespace liba
{
namespace input
{
namespace hard
{
namespace directx8
{

static const std::string axis_x_name( "Axis_X" );
static const std::string axis_y_name( "Axis_Y" );
static const std::string axis_z_name( "Axis_Z" );
static const std::string left_button_name( "Left Button" );
static const std::string middle_button_name( "Middle Button" );
static const std::string right_button_name( "Right Button" );

Mouse::Mouse( IDirectInputDevice8 * device, const std::string & name )
:	device( device ),
	name( name ),
	valid( false )
{
	for( int i=0; i < 8; i++ )
		buttons[i] = false;

	if( this->device->SetDataFormat( &c_dfDIMouse2 ) != DI_OK )
	{
		this->device->Release(); this->device = 0;
		return;
	}

	if( device->Acquire() != DI_OK )
	{
		this->device->Release(); this->device = 0;
		return;
	}
	valid = true;
}

Mouse::~Mouse()
{
	if( device )
	{
		device->Unacquire();
		device->Release(); device = 0;
	}
}

void Mouse::poll( int ms_count )
{
    HRESULT hr;
	DIMOUSESTATE2 ms;

    // poll the mouse to read the current state
	hr = device->Poll();
	hr = device->GetDeviceState( sizeof(ms), &ms ); 
	if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
	{
		hr = device->Acquire();
		hr = device->GetDeviceState( sizeof(ms), &ms ); 
	}
	if( hr == DI_OK )
	{
		threads::Lock lock(poll_lock);

		if( accumulate_buffer.size() > 2 )
		{
			accumulate_buffer.pop_front();
		}
		accumulate_buffer.push_back( math::Vector3<float>( float( ms.lX ), float( ms.lY ), float( ms.lZ ) ) );

		for( int i = 0; i < 8; i++ )
		{
			bool new_value = ms.rgbButtons[i] & 0x80;

			if( new_value != buttons[i] )
			{
				switch( i )
				{
					case 0:
						input::Manager::add_event( input::Event( name, left_button_name, new_value ) );
						break;
					case 1:
						input::Manager::add_event( input::Event( name, right_button_name, new_value ) );
						break;
					case 2:
						input::Manager::add_event( input::Event( name, middle_button_name, new_value ) );
						break;
					default:
						{
							std::stringstream ss;
							ss << "Button" << (i-2);
							input::Manager::add_event( input::Event( name, ss.str(), new_value ) );
						}
						break;
				}
			}
			buttons[i] = new_value;
		}

		math::Vector3<float> triplet(0, 0, 0);
		for( int i=0; i != accumulate_buffer.size(); ++i )
			triplet += accumulate_buffer[i];
		triplet *= 0.5f;

		if( fabs(triplet.x) > std::numeric_limits<float>::epsilon() )
			input::Manager::add_event( input::Event( name, axis_x_name, triplet.x ) );

		if( fabs(triplet.y) > std::numeric_limits<float>::epsilon() )
			input::Manager::add_event( input::Event( name, axis_y_name, triplet.y ) );

		if( fabs(triplet.z) > std::numeric_limits<float>::epsilon() )
			input::Manager::add_event( input::Event( name, axis_z_name, triplet.z ) );
	}
}

double Mouse::get_axis_position( const std::string & axis_name )const
{
	threads::Lock lock(poll_lock);

	if( axis_name == axis_x_name )
	{
		float x = 0;
		for( int i=0; i != accumulate_buffer.size(); ++i )
			x += accumulate_buffer[i].x;
		return x * 0.5f;
	}
	if( axis_name == axis_y_name )
	{
		float y = 0;
		for( int i=0; i != accumulate_buffer.size(); ++i )
			y += accumulate_buffer[i].y;
		return y * 0.5f;
	}

	if( axis_name == axis_z_name )
	{
		float z = 0;
		for( int i=0; i != accumulate_buffer.size(); ++i )
			z += accumulate_buffer[i].z;
		return z * 0.5f;
	}

	if( axis_name == left_button_name )
		return buttons[0];

	if( axis_name == right_button_name )
		return buttons[1];

	if( axis_name == middle_button_name )
		return buttons[2];

	if( axis_name == "Button1" )
		return buttons[3];

	if( axis_name == "Button2" )
		return buttons[4];

	if( axis_name == "Button3" )
		return buttons[5];

	if( axis_name == "Button4" )
		return buttons[6];

	if( axis_name == "Button5" )
		return buttons[7];

	return 0;
}

Manager::AxisType Mouse::get_axis_type( const std::string & axis_name )const
{
	if( axis_name == axis_x_name )
		return Manager::INF_AXIS;

	if( axis_name == axis_y_name )
		return Manager::INF_AXIS;

	if( axis_name == axis_z_name )
		return Manager::INF_AXIS;

	if( axis_name == "Left Button" )
		return Manager::BUTTON;
	
	if( axis_name == "Right Button" )
		return Manager::BUTTON;
	
	if( axis_name == "Middle Button" )
		return Manager::BUTTON;
	
	if( axis_name == "Button1" )
		return Manager::BUTTON;
	
	if( axis_name == "Button2" )
		return Manager::BUTTON;
	
	if( axis_name == "Button3" )
		return Manager::BUTTON;
	
	if( axis_name == "Button4" )
		return Manager::BUTTON;
	
	if( axis_name == "Button5" )
		return Manager::BUTTON;

	return Manager::NONE;
}

std::string Mouse::get_name()const
{
	return name;
}

} // namespace directx8
} // namespace hard
} // namespace input
} // namespace liba

